/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package edu.scs.ubbcluj.model;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import edu.scs.ubbcluj.backend.MongoDB;
import java.io.Serializable;

/**
 *
 * @author Torok
 */
public class Table implements Serializable, Model{
    private static final String TYPE = "TABLE";
    private String name;
    private String indexFileName;
    private List<Attribute> attributes;
    private List<Attribute> primaryKeys;
    private List<ForeignKey> foreignKeys;
    private List<Attribute> indexedAttributes;

    public Table(String name) {
        this.name = name;
        attributes = new ArrayList();
        primaryKeys = new ArrayList();
        foreignKeys = new ArrayList();
        indexedAttributes = new ArrayList();
        indexFileName = name + "Table.ind";
    }
    
    public List<String> getAllAttributesName() {
        List<String> nameList = new ArrayList();
        Iterator<Attribute> iterator = primaryKeys.iterator();
	while (iterator.hasNext()) {
            nameList.add(iterator.next().getName());
	}
        iterator = attributes.iterator();
	while (iterator.hasNext()) {
            nameList.add(iterator.next().getName());
	}
        Iterator<ForeignKey> iterator2 = foreignKeys.iterator();
	while (iterator2.hasNext()) {
            nameList.add(iterator2.next().getName());
	}
        return nameList;
    }
    
    public List<Attribute> getAllAttributes() {
        List<Attribute> list = new ArrayList();
        list.addAll(primaryKeys);
        list.addAll(attributes);
        list.addAll(foreignKeys);
        return list;
    }

    public List<Attribute> getAttributes() {
        return attributes;
    }

    public void addAttribute(Attribute attribute){
        attributes.add(attribute);
    }
    
    public void removeAttribute(Attribute attribute){
        attributes.remove(attribute);
    }
    
    public void setAttributes(List<Attribute> attributes) {
        this.attributes = attributes;
    }

    public List<Attribute> getPrimaryKeys() {
        return primaryKeys;
    }

    public void setPrimaryKeys(List<Attribute> primaryKeys) {
        this.primaryKeys = primaryKeys;
    }
    
    public void addPrimaryKey(Attribute attribute){
        primaryKeys.add(attribute);
    }
    
    public void removePrimaryKey(Attribute attribute){
        primaryKeys.remove(attribute);
    }
    
    public boolean isIndexed(Attribute attribute){
        return indexedAttributes.contains(attribute);
    }
    
    public Attribute isIndexed(ForeignKey attribute){
        for(Attribute a:indexedAttributes)
            if(a.getName().equals(attribute.getName()))
                return a;
        return null;
    }
    
    public List<String> getPrimaryKeysName() {
        List<String> nameList = new ArrayList();
        Iterator<Attribute> iterator = primaryKeys.iterator();
	while (iterator.hasNext()) {
		nameList.add(iterator.next().getName());
	}
        return nameList;
    }

    public List<ForeignKey> getForeignKeys() {
        return foreignKeys;
    }
    
    public List<Attribute> getForeignKeysRef() {
        return (List<Attribute>)(List<?>) foreignKeys;
    }

    public void setForeignKeys(List<ForeignKey> foreignKeys) {
        this.foreignKeys = foreignKeys;
    }
    
    public void addForeignKey(ForeignKey attribute){
        foreignKeys.add(attribute);
    }
    
    public void removeForeignKey(ForeignKey attribute){
        foreignKeys.remove(attribute);
    }
    
    public List<Attribute> getindexedAttributes() {
        return indexedAttributes;
    }

    public void setindexedAttributes(List<Attribute> primaryKeys) {
        this.indexedAttributes = primaryKeys;
    }
    
    public void addIndexedAttributes(Attribute attribute){
        if(!indexedAttributes.contains(attribute))
            indexedAttributes.add(attribute);
    }
    
    public void addIndexedAttributes(ForeignKey attribute){
        for(Attribute attr:indexedAttributes)
            if(attr.getName().equals(attribute.getName()))
                return;
        indexedAttributes.add(attribute);
    }
    
    public void removeIndexedAttributes(Attribute attribute){
        indexedAttributes.remove(attribute);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public String getIndexFileName() {
        return indexFileName;
    }

    public void setIndexFileName(String name) {
        this.indexFileName = name;
    }
    
    @Override
    public String toString() {
        return this.name;
    }

    @Override
    public String getType() {
       return TYPE;
    }
    
    public String checkUniqValBeforeInsert(Attribute attr, String value, int i){
        if(attr.isUnique()) {
            if(isIndexed(attr)) {
                if(MongoDB.keyExistsInIndex(getName()+attr.getName()+".ind", MongoDB.construct(attr, value)))
                    return attr.getName() + " is unique; " + value + " already exists."; 
            } else {           
                if(MongoDB.checkIfTableContains(this, i, MongoDB.construct(attr, value)))
                    return attr.getName() + " is unique; " + value + " already exists.";
            }
        }
        return null;
    }
    
    public boolean checkIfFkExistsInTable(ForeignKey fk, String value, int i) {
        Attribute a = isIndexed(fk);
        if(a != null)
            return MongoDB.keyExistsInIndex(getName()+a.getName()+".ind", MongoDB.construct(a, value));
        return MongoDB.checkIfTableContains(this, i, MongoDB.construct(fk, value));
    }
    
    public String checkFKeyBeforeInsert(ForeignKey attr, String value, int i){
        if(MongoDB.getRecordById(attr.getTable(), value) == null)
            return attr.getName() + " is a foreign key; the value " + value + " does not exists in table " + attr.getTable();
        Attribute a = isIndexed(attr);
        if(a != null) {
            if(attr.isUnique())
                if(MongoDB.keyExistsInIndex(getName()+attr.getName()+".ind", MongoDB.construct(attr, value)))
                    return attr.getName() + " is unique; " + value + " already exists.";
            return "ind";
        } else {
            if(attr.isUnique())
                if(MongoDB.checkIfTableContains(this, i, MongoDB.construct(attr, value)))
                    return attr.getName() + " is unique; " + value + " already exists.";
        }
        return null;
    }
    
    public String checkRowBeforeInsert(List<String> row){
        int i = 0;
        String ret = null;
        List<Attribute> indAttr = new ArrayList();
        List<String> indkeys = new ArrayList();
        for (int j = 0; j < this.getPrimaryKeys().size(); j++) {
            if(MongoDB.getRecordById(this, row.get(i)) != null)
                return getPrimaryKeys().get(j).getName() + " is unique primary key; " + row.get(i) + " already exists.";
            ret = this.getPrimaryKeys().get(j).checkAttributeValue(row.get(i));
            if(ret != null)
                return ret;
            i++;
        }
        for (int j = 0; j < this.getAttributes().size(); j++) {
            ret = checkUniqValBeforeInsert(this.getAttributes().get(j), row.get(i), i);
            if(ret != null)
                return ret;
            ret = this.getAttributes().get(j).checkAttributeValue(row.get(i));
            if(ret != null) {
                if(ret.equals("ind")) {
                    indAttr.add(this.getAttributes().get(j));
                    indkeys.add(row.get(i));
                }
                else
                    return ret;
            }
            i++;
        }
        for (int j = 0; j < this.getForeignKeys().size(); j++) {   
            ret = this.getForeignKeys().get(j).checkAttributeValue(row.get(i));
            if(ret != null)
                return ret;
            ret = checkFKeyBeforeInsert(this.getForeignKeys().get(j), row.get(i), i);
            if(ret != null) {
                if(ret.equals("ind")) {
                    indAttr.add(this.getForeignKeys().get(j));
                    indkeys.add(row.get(i));
                }
                else
                    return ret;
            }
            i++;
        }
        insertRowIntoIndex(indAttr, indkeys, MongoDB.construct(getPrimaryKeys(), row));
        return null;
    }
    
    public void insertRowIntoIndex(List<Attribute> attr, List<String> keys, String value) {
        for(int i = 0; i < attr.size(); i++)
            if(attr.get(i).isUnique())                
                MongoDB.createIndexOnUniqueKeyOneValue(this, attr.get(i), MongoDB.construct(attr.get(i), keys.get(i)), value);
            else
                MongoDB.createIndexNonOnUniqueKeyOneValue(this, attr.get(i), MongoDB.construct(attr.get(i), keys.get(i)), value);
    }
    
    public void deleteRowFromIndex(List<String> row) {
        String value = MongoDB.construct(getPrimaryKeys(), row);
        int i = getPrimaryKeys().size();
        for(Attribute atr:getAttributes()) {
            if(getindexedAttributes().contains(atr))
                if(atr.isUnique())
                    MongoDB.deleteIndexedUniqueValue(getName()+atr.getName()+".ind", MongoDB.construct(atr, row.get(i)));
                else
                    MongoDB.deleteIndexedNonUniqueValue(getName()+atr.getName()+".ind", MongoDB.construct(atr, row.get(i)), value);
        }
        for(Attribute atr:getForeignKeys()) {
            for(Attribute a:indexedAttributes) {
                if(a.getName().equals(atr.getName())) {
                    if(atr.isUnique())
                        MongoDB.deleteIndexedUniqueValue(getName()+atr.getName()+".ind", MongoDB.construct(atr, row.get(i)));
                    else
                        MongoDB.deleteIndexedNonUniqueValue(getName()+atr.getName()+".ind", MongoDB.construct(atr, row.get(i)), value);
                    break;
                }
            }
        }
    }
}